# Virtual Router Simulator 
#
# Copyright (C) 2009-2010 VRSim Project
# Author: Choonho Son <choonho@kt.com>
# URL: <http://vrsim.googlecode.com>
# For license information, see LICENSE.TXT
#

##
import datetime
import VRSim.Globals as globals
from PyQt4 import QtGui,  QtCore
from xml.dom.minidom import parse,  getDOMImplementation

from VRSim.Node.Node import Node
from VRSim.Node.XML import XML
from VRSim.Link.Ethernet import Ethernet
from VRSim.Utils import debug,  message
from VRSim.Ui.LinkConnection import LinkConnection
from VRSim.VM.VMAPIMon import VMAPIMon


class Topology(QtGui.QGraphicsScene,  XML):
    
    def __init__(self,  parent=None):
        QtGui.QGraphicsScene.__init__(self,  parent)
        # XML
        XML.__init__(self, 'Topology')
        
        self.__nodes = {}
        self.__links = set()
        
        self.node_baseid = 1
        self.link_baseid = 0
        self.changed = False
        self.binding_id = 0
        
        # XML elements
        self.location = "Korea"
        self.geoX = 37.0
        self.geoY = 126.0   
        # FORMAT: [name, hypervisor type, uri, VMAPI uri]
        #self.hypervisors = [["kvm1",  "kvm",  "qemu+proxy:///220.123.31.123:8000", 'http://220.123.31.123:8001/VMAPI'], 
        #                    ["dynamips1", "Dynamips",  "220.123.31.123:7200", "http://220.123.31.123:8001/VMAPI"] ]
        
        self.hypervisors = []
        #self.id = None
        #self.passwd = None
        self.vmapi = {}
        #self.updateVMAPI()
        self.designer = "Put your name"
        self.date = datetime.date.today()
        
        self.setSceneRect(-1000, -500, 2000, 1000)

    def init(self):
        # user's working directory
        userid = globals.GApp.userid
        globals.working_directory = "%s/%s" % (globals.BASEDIR,  userid) 
        # designer
        
    def updateVMAPI(self):
        for hypervisor in globals.GApp.default_hypervisors:
            index = hypervisor
            print "updateVMAPI %s" % index
            tf = self.vmapi.has_key(index)
            if tf == True:
                # already exist vmapi
                return True
                
            instance = VMAPIMon(index)
            # check initialzed is success?
            if instance.isInitialized == True:
                # push in a vmapi structure (resource manager)
                self.vmapi[index] = instance 
                
            else:
                return False
        return True
        
        
    def updateResourceURL(self,  url,  uri):
        """ update hypervisor information
        url = [ ["kvm1", "kvm", "qemu+proxy:////"  ], ["dynamips1", "dynamips", "220.123.31.xx:8011"] ......]
        uri = 'http://122.222.222:8000:VMAPI'
        """
        print "updateResourceURL: ",  url,  uri
        for index in url:
            index.append(uri)
            print index
            self.hypervisors.append(index)
        # TEST
        print "My hypervisors"
        print self.hypervisors
        
    def getNode(self,  id):
        if self.__nodes.has_key(id):
            return self.__nodes[id]
        else:
            return None
    
    def getNodeID(self,  node_name):
        for (id,  node) in globals.GApp.topology.nodes.iteritems():
            if node.physnode.hostname == node_name:
                return (id)
        return None
        
    def getNodeName(self,  nodeid):
        if self.__nodes.has_key(nodeid):
            node = self.__nodes[nodeid]
            return node.physnode.hostname
        else:
            msg = "Node ID(%s) is not exist" % nodeid
            globals.GApp.my_logger.error(msg)
            return "Not exist"
            
        
    def __getNodes(self):
        return self.__nodes
        
    def __setNodes(self,  value):
        self.__nodes = value
        
    nodes = property(__getNodes,  __setNodes,  doc='Property of nodes topology')
    
    def __getLinks(self):
        return self.__links
        
    links = property(__getLinks,  doc='Property of Links topology')
    
    def addNode(self,  node):
        print node.nodeid
        try:
            if isinstance(node,  Node):
                print "OK"
                QtCore.QObject.connect(node.guiinfo,  QtCore.SIGNAL("Add link"),  globals.GApp.scene.slotAddLink)
               #QtCore.QObject.connect(node,  QtCore.SIGNAL("Delete link"),  globals.GApp.scene.slotDeleteLink)
                self.__nodes[node.nodeid] = node
                self.addItem(node.guiinfo)
                msg = globals.eventMsg["addNode"]
                nmsg = msg % (node.physnode.hostname)
                globals.GApp.mainWindow.NodesLog.addEvent(nmsg)
                
        except :
            globals.err = "Fail to Add Node"
            globals.GApp.mainWindow.NodesLog.addError(globals.err)
            return False
            
        self.changed = True
        
        globals.GApp.mainWindow.NodesSummary.refresh()
        globals.GApp.mainWindow.NodesLog.refresh()
        
        return True
        
    def deleteNode(self,  id):
        """ delete node from topology
        """
        node = self.__nodes[id]
        # TODO: check deletable
        
        self.removeItem(node.guiinfo)
        del self.__nodes[id]
        
        msg = globals.eventMsg["deleteNode"]
        nmsg = msg % (node.physnode.hostname)
        globals.GApp.mainWindow.NodesLog.addEvent(nmsg)
        
        globals.GApp.mainWindow.NodesSummary.refresh()
        
        Dialog_RemoteFolder.resize(400, 300)
        self.verticalLayout = QtGui.QVBoxLayout(Dialog_RemoteFolder)
        self.verticalLayout.setObjectName("verticalLayout")
        self.widget = QtGui.QWidget(Dialog_RemoteFolder)
        self.widget.setObj
        
    def recordLink(self,  srcid,  srcif,  dstid,  dstif,  src_node,  dest_node,  type,  delay=0,  background=False):
        debug(globals.currentLinkType)
        # check link type
        if type:
            globals.currentLinkType = globals.linkTypes[type]
            
        if globals.currentLinkType == globals.Enum.LinkType.Ethernet or \
            globals.currentLinkType == globals.Enum.LinkType.FastEthernet or \
            globals.currentLinkType == globals.Enum.LinkType.GigaEthernet :
            
            link = Ethernet(globals.currentLinkType,  self.__nodes[srcid].guiinfo,  srcif,  self.__nodes[dstid].guiinfo,  dstif)

     
        # assign port number
        # this is UDP port assignment for single mode
        """
        if link.srcIf == None or background == True:
            link.srcIfPort = globals.binding_port + self.binding_id
            self.binding_id += 1
        if link.destIf == None or background == True:
            link.destIfPort = globals.binding_port + self.binding_id
            self.binding_id += 1
        """
        if link.srcIf == None or background == True:
            link.srcIfPort = self.getUDPPort(srcid)
            debug("SrcNode: Assigned UDP Port:" + str(link.srcIfPort))
            # update Physnode UDP info
            
        if link.destIf == None or background == True:
            link.destIfPort = self.getUDPPort(dstid)
            debug("DstNode: Assigned UDP Port:" + str(link.destIfPort))
            # update physnode info
        
        if background == True:
            # update SrcNode UDP Port
            (s, p) = link.getSrcSlotNPort()
            # get Interface instance
            node = self.nodes[srcid]
            interfaceInst = node.physnode.devices.getInterface(s, p)
            interfaceInst.setInternal(link.srcIfPort,  link.destIfPort)
            
            # update DestNode UDP Port
            (s,  p) = link.getDstSlotNPort()
            # get Interface instance
            node = self.nodes[dstid]
            interfaceInst = node.physnode.devices.getInterface(s,  p)
            interfaceInst.setInternal(link.destIfPort,  link.srcIfPort)
            
        #self.allocateLinkAttribute(link.type ,  link.source.nodeid,  link.srcIf,  link.dest.nodeid,  link.destIf)
        if background == False:
            ok = self.allocateLinkAttribute(link)
            # does not want to make link
            if ok == False:
                return
        else:
            link.delay = delay
            link.updateLabel()
            
        
        self.__links.add(link)
        self.addItem(link)
        
        
    def addLink(self,  srcid,  srcif,  dstid,  dstif,  type=None,  delay=0,  background=False):
        src_node = globals.GApp.topology.getNode(srcid)
        dst_node = globals.GApp.topology.getNode(dstid)
        
        self.recordLink(srcid,  srcif,  dstid,  dstif,  src_node,  dst_node,  type,  delay,  background)
        
        #globals.GApp.mainWindow.treeWidget_topologySummary.refresh()
        self.changed = True
        
        # Event Log
        msg = globals.eventMsg["addLink"]
        nmsg = msg % (src_node.physnode.hostname,  dst_node.physnode.hostname)
        globals.GApp.mainWindow.NodesLog.addEvent(nmsg)
        
        return True

    def deleteLink(self,  link):
        # delete edge information from nodes
        # GUIInfo
        link.source.deleteEdge(link)
        link.dest.deleteEdge(link)
        # delete domain information
        srcNode = globals.GApp.topology.nodes[link.source.nodeid]
        dstNode = globals.GApp.topology.nodes[link.dest.nodeid]
        
        srcNode.physnode.devices.clearInterface(link.srcIf)
        dstNode.physnode.devices.clearInterface(link.destIf)
        
        # delete from GUI
        # delete If name also
        if link in self.__links:
            self.__links.remove(link)
            self.removeItem(link.labelSrcIf)
            self.removeItem(link.labelDstIf)
            self.removeItem(link)
            
            # Event Log
            msg = globals.eventMsg["deleteLink"]
            nmsg = msg % (srcNode.physnode.hostname,  dstNode.physnode.hostname)
            globals.GApp.mainWindow.NodesLog.addEvent(nmsg)
        
        return True
        

    def allocateLinkAttribute(self,  link):
        """ Assign udp socket number for
        internal socket connection
        """
        #src_pnode = globals.GApp.topology.nodes[srcid].physnode
        #dst_pnode = globals.GApp.topology.nodes[destid].physnode
        
        cfg = LinkConnection(link)
        #cfg.show()
        rvalue = False
        if cfg.exec_():
            rvalue = cfg.isSaved
        return rvalue
     
    def getVMName(self,  type):
        """ return VMName from hypervisors
        type: {kvm, Dynamips}
        return: {kvm1 | dynamips1 ...}
        """
        for hyper in self.hypervisors:
            if type == hyper[1]:
                return hyper[0]
      
    def getVMType(self,  vmname):
        """ return VMtype from vmname
        vmname: { kvm1 | dynamips1 ...}
        return: {kvm| Dynamips}
        """
        for hyper in self.hypervisors:
            if vmname == hyper[0]:
                return hyper[1]
    # XML
    def getXML(self):
        if self.xml:
            self.xml.unlink()
            
        # debug
        globals.GApp.my_logger.debug("topology : getXML")
        obj = getDOMImplementation()
        self.xml = obj.createDocument(None,  self.title,  None)
        self.root = self.xml.getElementsByTagName(self.title)[0] 
        #self.xml.unlink()
        # <Info> xxx </Info>
        info = self.xml.createElement("Info")
        # <Geometry X=123  Y=123/>
        geo = self.xml.createElement("Geometry")
        geo.setAttribute("where",  str(self.location) )
        geo.setAttribute("X",  str(self.geoX))
        geo.setAttribute("Y",  str(self.geoY))
        info.appendChild(geo)
        
        # <Hypervisor name='HyperA' type='kvm' vmapi='http://127.0.0.1:8001/VMAPI'>URI </Hypervisor>
        # hypervisors = [ ['name','type' , 'uri', 'vmapi'] , .... ]
        """
        for node in self.hypervisors:
            hyper = self.xml.createElement("Hypervisor")
            hyper.setAttribute("name",  str(node[0]) )
            hyper.setAttribute("type",  str(node[1]) )
            hyper.setAttribute("vmapi",  str(node[3]))
            text = self.xml.createTextNode( str(node[2]) )
            hyper.appendChild(text)
            info.appendChild(hyper)
        """
        # hypervisor = ["http://220.xxx.xx.xxx:8000/VMAPI" ....]
        for node in globals.GApp.default_hypervisors:
            hyper = self.xml.createElement("Hypervisor")
            text = self.xml.createTextNode(node)
            hyper.appendChild(text)
            info.appendChild(hyper)
            
        # <Designer> Choonho Son</Designer>
        des = self.xml.createElement("Designer")
        text = self.xml.createTextNode(self.designer)
        des.appendChild(text)
        info.appendChild(des)
        
        # <Date>24 July, 2009</Date>
        date = self.xml.createElement("Date")
        text = self.xml.createTextNode(str(self.date))
        date.appendChild(text)
        info.appendChild(date)
        
        self.root.appendChild(info)
        
        # Nodes
        nodes = self.xml.createElement("Nodes")
        for (nodeid, node) in self.__nodes.items():
            element = node.getXML()
            nodes.appendChild(element)
        self.root.appendChild(nodes)
        
        # Links
        links = self.xml.createElement("Links")
        for link in self.__links:
            element = link.getXML()
            links.appendChild(element)
        self.root.appendChild(links)
        
        return self.root
        
    def setXML(self,  dom):
        # <Geometry X=123  Y=123/> 
        geo = dom.getElementsByTagName("Geometry")[0]
        self.location = geo.getAttribute("where")
        self.geoX = geo.getAttribute("X")
        self.geoY = geo.getAttribute("Y")
        
        # <Hypervisor name='HyperA' type='kvm' vmapi='http://127.0.0.1:8001/VMAPI'>URI </Hypervisor>
        # hypervisors = [ ['name','type' , 'uri'] , .... ]
        #self.hypervisors = []
        """
        for node in dom.getElementsByTagName("Hypervisor"):
            name = node.getAttribute("name")
            type = node.getAttribute("type")
            vmapi = node.getAttribute("vmapi")
            url = node.firstChild.data
            self.hypervisors.append([name, type,  url,  vmapi])
        """
        for node in dom.getElementsByTagName("Hypervisor"):
            vmapi = node.firstChild.data
            if not (vmapi in globals.GApp.default_hypervisors):
                # new VMAPI URI
                msg = "New VMAPI is not matched (%s)\ncorrect right VMAPI URL" % vmapi
                message(msg)
                return False
                
            
        # <Designer> Choonho Son</Designer>
        des = dom.getElementsByTagName("Designer")[0]
        self.designer = des.firstChild.data
        # <Date>24 July, 2009</Date>
        date = dom.getElementsByTagName("Date")[0]
        self.date = date.firstChild.data
        
        # recursive call of Nodes
        nodes = dom.getElementsByTagName("Node")
        for node in nodes:
            # extract node information
            hostname = node.getAttribute("name")
            node_type = node.getElementsByTagName("type")[0]
            typeValue = node_type.firstChild.data
            node_location = node.getElementsByTagName("location")[0]
            locX = node_location.getAttribute("X")
            locY = node_location.getAttribute("Y")
            
            
            nodeInstance = globals.GApp.scene.makeNode(typeValue)
            nodeInstance.guiinfo.updateHostname(hostname)
            nodeInstance.guiinfo.setPos(float(locX),  float(locY))
            
            nodeInstance.setXML(node)
            # push into topology
            self.addNode(nodeInstance)
            
            
            # update domain information
            
        
        # recursive call of Links
        links = dom.getElementsByTagName("Link")
        for link in links:
            # make link instance
            print link.toprettyxml()
            type = link.getAttribute("type")
            nodeA = link.getAttribute("nodeA")
            nodeB = link.getAttribute("nodeB")
            nodeAIF = link.getAttribute("nodeAIF")
            nodeBIF = link.getAttribute("nodeBIF")
            delay = link.getAttribute("delay")
            srcid = self.getNodeID(nodeA)
            dstid = self.getNodeID(nodeB)
            
            self.addLink(srcid,  nodeAIF,  dstid,  nodeBIF,  type,  delay,  background=True)
            
        
    # VMAPI
    def getVMAPI(self,  nodeid):
        """ return VMAPI instance
        from instance, you can call xmlrpc
        """
        node = self.__nodes[nodeid]
        vmname = node.vm
        vmapi = self.getVMAPIURI(vmname) 
        inst = self.vmapi[vmapi]
        return inst
        
    def getVMAPIIP(self,  nodeid):
        """ return VMAPI's IP 
        """
        node = self.__nodes[nodeid]
        vmname = node.vm
        for hypervisor in self.hypervisors:
            if hypervisor[0] == vmname:
                apiuri = hypervisor[3]
                # http://xxx.xxx.xxx.xxx:port/VMAPI
                index = apiuri.split("://")
                ipindex = index[1].split(":")
                ip = ipindex[0]
                return ip
        # fail to find vm's ip
        msg = "getVMAPIIP: getVMAPIIP failed"
        return "127.0.0.1"
        
    def getConsolePort(self,  nodeid):
        debug("request Console port from: " + str(nodeid) )
        node = self.__nodes[nodeid]
        vmname = node.vm
        debug("vmname in getConsolePort:" + vmname)
        vmapi = self.getVMAPIURI(vmname)
        inst = self.vmapi[vmapi]
        tcpport = inst.requestTCP()
        return tcpport
        
    def getUDPPort(self,  nodeid):
        debug("request UDP port from: " + str(nodeid) )
        node = self.__nodes[nodeid]
        vmname = node.vm
        debug("vmname in getUDPPort:" + vmname)
        vmapi = self.getVMAPIURI(vmname)
        inst = self.vmapi[vmapi]
        udpport = inst.requestUDP()
        return udpport
        
    def getVMAPIURI(self,  vmname):
        debug("VMNAME:" + vmname)
        for hypervisor in self.hypervisors:
            if hypervisor[0] == vmname:
                return hypervisor[3]
        debug("getVMAPIURI: Unmatched vmname " + vmname)
        
        
        
